from textwrap import dedent
from unittest.mock import create_autospec

import boto3_type_annotations.s3
import pandas as pd
import pytest
from boto3_type_annotations.s3 import Client
from botocore.exceptions import ClientError

from src.s3.s3 import Boto3S3Uploader
from src.settings import RetrySettings, S3Settings


@pytest.fixture()
def boto():
    return create_autospec(boto3_type_annotations.s3.client.Client)


@pytest.fixture()
def uploader(boto: Client) -> Boto3S3Uploader:
    return Boto3S3Uploader(
        client=boto,
        s3_settings=S3Settings(access_key="access_key", secret_access_key="secret_access_key"),
        retry_settings=RetrySettings(),
    )


def test_if_bucket_exists(boto):
    boto.head_bucket.return_value = {}
    Boto3S3Uploader(
        client=boto,
        s3_settings=S3Settings(access_key="access_key", secret_access_key="secret_access_key"),
        retry_settings=RetrySettings(),
    )
    boto.create_bucket.assert_not_called()


def test_if_bucket_does_not_exist(boto):
    boto.head_bucket.side_effect = ClientError({}, "head")
    Boto3S3Uploader(
        client=boto,
        s3_settings=S3Settings(access_key="access_key", secret_access_key="secret_access_key"),
        retry_settings=RetrySettings(),
    )
    boto.create_bucket.assert_called_once_with(Bucket=S3Settings().bucket)


def test_upload_one_column_df(uploader, boto):
    uploader.upload(
        name="out_name",
        data=pd.DataFrame({"output": [1] * 10}).to_csv(header=True, index=False).encode(),
    )
    expected_data = (
        dedent(
            """
    output
    1
    1
    1
    1
    1
    1
    1
    1
    1
    1
    """
        )
        .lstrip()
        .encode("utf8")
    )
    boto.upload_fileobj.assert_called_once()
    assert boto.upload_fileobj.call_args[1]["Fileobj"].getvalue() == expected_data


def test_upload_several_column_df(uploader, boto):
    uploader.upload(
        name="out_name",
        data=pd.DataFrame(
            {"input_column": ["test" * i for i in range(10)], "output": [1] * 10}
        )
        .to_csv(header=True, index=False)
        .encode(),
    )
    expected_data = (
        dedent(
            """
    input_column,output
    ,1
    test,1
    testtest,1
    testtesttest,1
    testtesttesttest,1
    testtesttesttesttest,1
    testtesttesttesttesttest,1
    testtesttesttesttesttesttest,1
    testtesttesttesttesttesttesttest,1
    testtesttesttesttesttesttesttesttest,1
    """
        )
        .lstrip()
        .encode("utf8")
    )
    boto.upload_fileobj.assert_called_once()
    assert boto.upload_fileobj.call_args[1]["Fileobj"].getvalue() == expected_data
